﻿using Org.BouncyCastle.Utilities.Encoders;
using System.ComponentModel;
using System.Text;
using System.Web;

namespace PmSoft.Utilities.Sm4;

/// <summary>
/// Sm4算法 对标国际DES算法
/// </summary>
public class Sm4Crypto
{
    public Sm4Crypto()
    {
        //1.0.8
        Key = "7361646633326173646a6e6b73616764";//2023-12-6
        Iv = "sbnty74299507719";
        //Key = "6166733764734d734a33357951363544";
        //Iv = "sbnty74299507719";
        HexString = true;
        CryptoMode = Sm4CryptoMode.CBC;
    }

    /// <summary>
    /// 数据
    /// </summary>
    public string Data { get; set; }

    /// <summary>
    /// 秘钥
    /// </summary>
    public string Key { get; set; }

    /// <summary>
    /// 向量
    /// </summary>
    public string Iv { get; set; }

    /// <summary>
    /// 明文是否是十六进制
    /// </summary>
    public bool HexString { get; set; }

    /// <summary>
    /// 加密模式(默认ECB)
    /// </summary>
    public Sm4CryptoMode CryptoMode { get; set; }

    #region 加密

    public string Encrypt(Sm4Crypto entity)
    {
        return entity.CryptoMode == Sm4CryptoMode.CBC ? EncryptCBC(entity) : EncryptECB(entity);
    }

    /// <summary>
    /// ECB加密
    /// </summary>
    /// <param name="entity"></param>
    /// <returns></returns>
    public static string EncryptECB(Sm4Crypto entity)
    {
        Sm4Context ctx = new Sm4Context
        {
            IsPadding = true
        };

        byte[] keyBytes = entity.HexString ? Hex.Decode(entity.Key) : Encoding.Default.GetBytes(entity.Key);

        SM4 sm4 = new SM4();
        sm4.SetKeyEnc(ctx, keyBytes);
        byte[] encrypted = sm4.Sm4CryptEcb(ctx, Encoding.Default.GetBytes(entity.Data));

        return Encoding.Default.GetString(Hex.Encode(encrypted));
    }

    /// <summary>
    /// CBC加密
    /// </summary>
    /// <param name="entity"></param>
    /// <returns></returns>
    public static string EncryptCBC(Sm4Crypto entity)
    {
        Sm4Context ctx = new Sm4Context
        {
            IsPadding = true
        };

        byte[] keyBytes = entity.HexString ? Hex.Decode(entity.Key) : Encoding.Default.GetBytes(entity.Key);
        byte[] ivBytes = Encoding.Default.GetBytes(entity.Iv);
        // byte[] ivBytes = entity.HexString ? Hex.Decode(entity.Iv) : Encoding.Default.GetBytes(entity.Iv);

        SM4 sm4 = new SM4();
        sm4.SetKeyEnc(ctx, keyBytes);
        byte[] encrypted = sm4.Sm4CryptCbc(ctx, ivBytes, Encoding.Default.GetBytes(entity.Data));
        return Encoding.Default.GetString(Hex.Encode(encrypted));
        // return encrypted.ToBase64();
    }

    #endregion 加密

    #region 解密

    public object Decrypt(Sm4Crypto entity)
    {
        return entity.CryptoMode == Sm4CryptoMode.CBC ? DecryptCBC(entity) : DecryptECB(entity);
    }

    /// <summary>
    /// ECB解密
    /// </summary>
    /// <param name="entity"></param>
    /// <returns></returns>
    public static string DecryptECB(Sm4Crypto entity)
    {
        Sm4Context ctx = new Sm4Context
        {
            IsPadding = true,
            Mode = 0
        };

        byte[] keyBytes = entity.HexString ? Hex.Decode(entity.Key) : Encoding.Default.GetBytes(entity.Key);

        SM4 sm4 = new SM4();
        sm4.Sm4SetKeyDec(ctx, keyBytes);
        byte[] decrypted = sm4.Sm4CryptEcb(ctx, Hex.Decode(entity.Data));
        return Encoding.Default.GetString(decrypted);
    }

    /// <summary>
    /// CBC解密
    /// </summary>
    /// <param name="cipherText"></param>
    /// <returns></returns>
    public static string DecryptCBC(Sm4Crypto entity)
    {
        Sm4Context ctx = new Sm4Context
        {
            IsPadding = true,
            Mode = 0
        };

        byte[] keyBytes = entity.HexString ? Hex.Decode(entity.Key) : Encoding.Default.GetBytes(entity.Key);
        byte[] ivBytes = Encoding.Default.GetBytes(entity.Iv);
        //byte[] ivBytes = entity.HexString ? Hex.Decode(entity.Iv) : Encoding.Default.GetBytes(entity.Iv);

        SM4 sm4 = new SM4();
        sm4.Sm4SetKeyDec(ctx, keyBytes);
        byte[] decrypted = sm4.Sm4CryptCbc(ctx, ivBytes, Hex.Decode(entity.Data));
        return Encoding.Default.GetString(decrypted);
    }

    #endregion 解密

    public static string UrlEncode(string str)
    {
        StringBuilder builder = new StringBuilder();
        foreach (char c in str)
        {
            if (HttpUtility.UrlEncode(c.ToString()).Length > 1)
            {
                builder.Append(HttpUtility.UrlEncode(c.ToString()).ToUpper());
            }
            else
            {
                builder.Append(c);
            }
        }
        return builder.ToString();
    }

    public static string UrlDecode(string str)
    {
        StringBuilder builder = new StringBuilder();
        foreach (char c in str)
        {
            if (HttpUtility.UrlDecode(c.ToString()).Length > 1)
            {
                builder.Append(HttpUtility.UrlEncode(c.ToString()).ToUpper());
            }
            else
            {
                builder.Append(c);
            }
        }
        return builder.ToString();
    }

    /// <summary>
    /// 加密类型
    /// </summary>
    public enum Sm4CryptoMode
    {
        /// <summary>
        /// ECB(电码本模式)
        /// </summary>
        [Description("ECB模式")]
        ECB = 0,

        /// <summary>
        /// CBC(密码分组链接模式)
        /// </summary>
        [Description("CBC模式")]
        CBC = 1
    }
}